home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The X-Philes (2nd Revision)
/
The X-Philes Number 1 (1995).iso
/
xphiles
/
hp48hor1
/
rpl3.doc
< prev
next >
Wrap
Text File
|
1995-03-31
|
14KB
|
312 lines
(Comp.sys.handhelds)
Item: 2282 by bson at rice-chex.ai.mit.edu
Author: [Jan Brittenson]
Subj: Some HP-48 Internals Answers
Date: Fri Mar 01 1991
Joe Ervin asked me some questions in a letter. It seemed to me that
the questions were fundamental enough to be answered on the net - I
haven't seen any in-depth discussions of these topics before; I hope
Joe will excuse me, and the rest of you bear with me. If you hate
everything related to HP-48 internals, hit the proper Junk key now. I
will stick to STAR syntax and AG mnemonics.
> In all the examples of system RPL I have seen, I have NOT seen any
> loops or IF-THEN constructs, although I believe that Bill Wickes has
> IF-THEN in his ASC routines.
I recently posted a set of FFT-aiding routines here on
comp.sys.handhelds. Two of them use if-then constructs, and one has a
loop as well as local variables. The latter took quite some exper-
imentation to get right!
Here is one of them, it collapses an array into a one-dimensional
list (I have reduced it to bare bones here - let's not bother with the
actual addresses):
RPL
Prg ; PROGRAM
SaveLast_Need_1_arg ; Housekeeping stuff
ArrayTo_array ; Explode array
ListAlgPrgTo ; Explode dimensions list
Equal2p
If_then_else ; Then: (real->short) * (real->short)
Prg
Real_to_short_Swap
Real_to_short
Mul
End
Real_to_short ; Else: real->short
ToList ; Combine to list
End ; END
ENDRPL
Now, broken down step by step:
RPL Enters STAR RPL mode.
Prg Start of program object (2d9d).
SaveLast_Need_1_arg
Makes sure we have at least one argument.
ArrayTo_array
Explodes the array - ARRY-> on an array. This yields a list of
reals containing dimensional info, e.g. {2 3} for a 2-by-3
array.
ListAlgPrgTo
Explodes the list - LIST-> on a list. Like the user-mode
command, expcept the the counter on level 1 is a short.
Equal2p If level 1 is the short 2, then return True, else False. True
is #3a81 and False is #3ac0. These both appear as "External"
on the stack, and can't be entered from user code. Like shorts
(system binaries) are preferred internally for small integers,
such as the list count above, True and False is the system RPL
preferred way of passing around boolean flags.
If_then_else (#61ad8)
If level 1 is True (#3a81) then execute the next object,
otherwise execute the second next object.
Prg Start of program object (2d9d). This program object is the
then-clause. This gets evaluated if the list contained two
elements, i.e. was 2-dimensional. Otherwise it was one-
dimensional.
Real_to_short_Swap
(Then-clause) Convert a real (from the array dimensions list)
to a short and do a swap.
Real_to_short
(Then-clause) Convert the second real to a short.
Mul (Then-clause) Multiply the two shorts. Notice that we could
just as well have multiplied the two reals (using the user-
code real multiplication function) and then converted the
result to a short. But short (20-bit unsigned integer)
mulitplication is magnitudes faster than real (64-bit BCD)
multiplication.
End End (#312b) of the then-clause program object.
Real_to_short
This is the else-clause. The array was 1-dimensional, so
we just convert the size to a short.
ToList Finally we put it all back to a list. The item count on
level 1 is assumed to be a short.
End This is the end of our program, and the end of the object.
ENDRPL Signals end of STAR RPL mode.
> It looks like in the system RPL, you cannot simply push an object
> onto the stack the way you do in user code.
Sure you can. System RPL executes almost identically to user RPL.
The big difference is that you can't automatically interrupt it with
ATTN - the system RPL has to explicitly check for this. System RPL
also has a lot of constructs for implementing control and branch
functions - you can, for instance, write the If_then_else above in
system RPL by calling more primitive functions like Skip (which skips
the next token in the *caller's* thread), or pick the next token from
the caller's thread, or evaluate the token in the caller's thread. A
token here is either an object or the 20-bit address of an object. In
fact, If_then_else *is* written in system RPL if I remember correctly.
There are more specialized examples; we could, for instance, have
written an If_equal2p_then_else function if we wanted (and had the
option of putting it in a fixed location in ROM!).
> This is apparantly because system RPL consists strictly (I think) of
> nibble addresses. Because of this, you can't really put an object
> into the thread, because the "threader", or whatever you call the code
> which does the JUMP @(next rpl token) won't know what to do with an
> object it wasn't expecting.
A token is always either a pointer to a _type_ or an object, in
which case its first subtoken, the prefix, is a type. There is never
more than one level of indirection here. The types (#2d92 for
programs, #2a2c for strings, #2dcc for code, etc) all begin with
#28fc, which indicates that "this is a type." The code at #28fc
implements the semantics of the _type_. What it mostly does is to call
the type-dependent code implementing the type. I have to confess I am
on very weak ice here, so don't take this as the ultimate truth, it's
merely based on my observations. It's fairly clear that what most data
types do, is to simply push themselves on the stack. Then there are
others (lists and symbolics) that sometimes push themselves on the
stack, and sometimes behave as programs. Finally there are those that
always result in code being executed - programs (#2d9d) and code
(#2dcc). Then we have global and local names that evaluate their
values.
I think that exactly how this works is best illustrated by an example.
The code at #28fc looks like this:
move.p2 10, c
sub.a c, a
jump @a
Assume that the next token is a program object. Then the sequence:
move.a @d0, a
add 5, d0
jump @a
Will 1) put the prefix (#2d9d) of the program in A. 2) Advance the
pointer (which now points immediately following the prefix). 3) Read
the contents of #2d9d (which is the "type" prefix #28fc) and jump
there (i.e. to #28fc). Upon arriving at #28fc, A will contain #2d9d.
10 is subtracted from it (the #28fc "type" prefix at #2d9d is preceded
by two addresses that implement certain semantics of "programs") to
yield the address of a pointer to the Eval-specific semantics for
"programs," i.e. a piece of ML code that does whatever "programs" are
supposed to do when they are executed (EVALed). This is where it then
jumps. D0 still points to the word following the prefix, and A is
#2d9d minus 10.
As to the composition of RPL objects, I would recommend you to take
a look at one of Derek Nickel's postings which explicitly addresses
this. It should suffice to mention here that program, symbolic, and
list objects are almost identical apart from the prefixes.
> There are some RPL tokens that require an argument, and the cases I
> have seen of this always have the argument appear as the next token.
> This is the case for the "type_check" token which appeared in your
> recent example code, and I strongly suspect that this works with some
> other tokens.
Quite correct. Sometimes it's even more complicated, like the type
check routines that take any paired sequence of short tokens and
tokens to be executed if a match was made; up to an end (#312b). If a
match is made the matching process ends and the subsequent token is
executed. Otherwise the current address (in D0) is advanced to the
next pair. When it reaches an end (#312b) a "bad argument type" error
is issued. Some of these type matching functions also play tricks
with the LAST ARG data.
> When I started looking at system RPL, I was of the belief that the
> system RPL would be a lot like the user RPL. This does not appear to
> be true. It seems that for any looping structure or IF-THEN structure,
> or any more complicated structure, it looks like the system RPL will
> require different techniques.
They are not really that much more complicated, just undocumented
and slightly different. They sometimes require knowledge of exactly
how they work to be taken full advantage of. One illustrative example
is loops. My understanding is that they basically consist of a head
and a tail. The head enters the loop (similar to START...NEXT in its
simpler form) and the tail terminates it. Now, what the head does more
exactly is to set the return address *to itself* and call the first
word of the loop. The end of the loop basically does an END, which
causes it to return to the loop head and reexecute it! Since a loop
call frame looks like any other call frame, the possibilities are
almost endless for implementing you own specialized NEXT, or STEP, or
UNTIL, or whatever your heart may desire. The tail can exit the loop
by unwinding one call frame level and continuing.
You can, if you like, implement a specialized NEXT (or other loop
controls) and put it in a global variable. You can even implement
specialized IFs, skips, what may you desire, as well, by putting them
in a global variables. A small example (make sure your HP-48 is set to
HEX!):
Key in PUSH (ASC format) and de-ASC it:
"CCD20310008F146608DC2016C1CD"
Type: #61ad8 #5a03 SYSEVAL @ --> <61ad8>
PUSH @ --> External
'IFFTE' STO
Now we have stored the If_then_else routine (#61ad8) in IFFTE, or
more precisely, a pointer to it. Type in the following program (notice
that True (#3a81) and False (#3ac0) push themselves on the stack when
evaluated):
<< IFFTE "TRUE" "FALSE" >> 'X' STO
Then let's try it out (the @#XXXX notation used means "the object
at address XXXX" - the display will actually read "External" in our
example below):
#3a81 SYSEVAL @ --> @#3a81 (True)
X @ --> "TRUE"
DROP
#3ac0 SYSEVAL @ --> @#3ac0 (False)
X @ --> "FALSE"
Voila! A new user RPL control structure! Notice that our user
program X above is identical to the following (STAR syntax), with the
exception that << and >> tokens have been added to the user program;
also note that an initial "_" character escapes the implicit STAR
DATA.A of RPL mode:
RPL
2d9d ; Program object
_global `IFFTE' ; Token: Global symbol object
_string `TRUE' ; Token: String object
_string `FALSE' ; Token: String object
312b ; End
ENDRPL
For clarification, the global name and strings above are made
in-line objects, not pointers.
> In fact, it appears that for many tasks, the normal user code is much,
> much simpler, and will probably run nearly as fast.
Simpler because they are more well-documented, yes. But not faster
by a long shot! System RPL mostly makes type checking superfluous,
except initially, and does most of its arithmetic using 20-bit
integers.
> Surely there are many highly useful ROM routines that will be nice to
> use, but aren't those generally accessible from user code using
> SYSEVAL?
Yes, of course. But who can read a program consisting solely on
SYSEVALs? Binary integers and SYSEVALs take a lot more space and are
much slower, although they can be "compiled" using Joseph Horn's PACK
program. I personally tend to test algorithms and generally make
prototypes in user RPL, and then simply recode it in system RPL for
robustness, speed, and size. Not everything of course, I find it
generally desirable to code the "workhorse" routines in system RPL and
call them from user RPL that can easily be customized.
> One key advantage with system RPL for me is that it can hold a CODE
> object; a feat of which user code does not appear to be capable.
This is most useful indeed. And while you're at it, with assemblers
and all, you may just as well do the rest in system RPL. Or at least
embed the ML in some argument checking.
> I will refrain from asking you anymore direct questions for now, for
> fear that I will totally overwhelm you.
Au contraire; I'm glad you posed these questions. I remember
wondering over this too, and I'm sure a lot of people want to know. I
don't claim to know that much about how the HP-48 works; Rick Grevelle
and a dozen other net people probably know much more than I do.
Perhaps explaining the basics will get others started, I hope so -
which is why I spent a little extra effort on this reply (as well as
posted it to the net). I hope I am correct - I'm sure I will quickly
be corrected if I'm not!
-- Jan Brittenson
bson@ai.mit.edu